/////////////////////////////////////////////////////////////////////////////////////
// Header Protection
#ifndef _MESHMANAGER_H_
#define _MESHMANAGER_H_

/////////////////////////////////////////////////////////////////////////////////////
// Include Files
#include "Event\Event.h"

#include "Rendering\GLExt.h"
//#include "GLee\GLee.h"

// Forward Declarations
class CMeshEventArg;

/////////////////////////////////////////////////////////////////////////////////////
// Structures

struct TMesh
{
	// name
	string_t strName;

	// vertex array object
	uint_t uiVAO;

	uint_t uiIBO;
	uint_t uiVBO;

	// drawing data
	uint_t uiIndexCount;
	GLenum eIndexType;
};

/////////////////////////////////////////////////////////////////////////////////////
// This class maintains a list of all loaded meshes and provides an indexing 
//     system in order to allow access to the meshes on an instanced basis.
class CMeshManager : public ISingleton<CMeshManager>
{
public:     // methods

	void Initialize();
	void Shutdown();

	void LoadMeshFile( string_t _strFilename );
	void CreateMesh( CMeshEventArg &_rArg );
	void UpdateMesh( CMeshEventArg &_rArg );

	// TODO
	//void FreeMesh( string_t _strName );

	TMesh *GetMesh( string_t _strName )
	{
		// TODO: this may be unsafe, depending on how it is used... returning a 
		//     pointer means it can be modified somewhere else in the middle of the 
		//     render.
		for( uint_t i = 0; i < m_lstMeshes.GetSize(); ++i )
		{
			if( m_lstMeshes[i].strName == _strName )
			{
				return &m_lstMeshes[i];
			}
		}
		return CE_NULL;
	}

public:     // events

	// A mesh has been loaded from a file and needs to be created.
	CEvent<CMeshEventArg> MeshLoad;

	// A mesh has been edited and needs to be updated
	CEvent<CMeshEventArg> MeshUpdate;

private:
	uint_t CreateBuffer( GLenum _eTarget, uint_t _uiSize, void *_pData, GLenum _eUsage );

private:    // friend methods
	friend class ISingleton<CMeshManager>;

	CMeshManager() { }
	~CMeshManager() { }

private:    // dummy definitions
	CMeshManager( CMeshManager const & );
	CMeshManager &operator =( CMeshManager const & );

private:    // data
	mutex_t       m_Mutex; // TODO: use this if necessary
	list_t<TMesh> m_lstMeshes;
};

class CMeshEventArg : public CEventArg
{
public:     // accessors
	inline string_t const &GetName() const
	{ return m_strName; }

	inline uint_t GetVertexDataSize() const
	{ return m_uVertexDataSize; }

	inline byte_t const *GetVertexData() const
	{ return m_pVertexData; }

	inline uint_t GetIndexDataSize() const
	{ return m_uIndexDataSize; }

	inline byte_t const *GetIndexData() const
	{ return m_pVertexData; }

public:     // methods

	// _strName : string_t - the name to use to reference this mesh
	inline CMeshEventArg( string_t _strName ) : 
	CEventArg(), m_strName(_strName), 
	m_uVertexDataSize(0), m_pVertexData(CE_NULL), 
	m_uIndexDataSize(0), m_pIndexData(CE_NULL)
	{ }

	inline ~CMeshEventArg()
	{
		delete[] m_pVertexData;
		delete[] m_pIndexData;
	}

	inline void SetVertexData( uint_t _uSize, byte_t *_pData )
	{
		m_uVertexDataSize = _uSize;
		m_pVertexData = _pData;
	}

	inline void SetIndexData( uint_t _uSize, byte_t *_pData )
	{
		m_uIndexDataSize = _uSize;
		m_pIndexData = _pData;
	}

private:    // data
	string_t m_strName;

	// TODO: vertex attributes... list maybe?

	// vertex data
	uint_t m_uVertexDataSize;
	byte_t *m_pVertexData;

	// index data
	uint_t m_uIndexDataSize;
	byte_t *m_pIndexData;

private:    // dummy definitions
	CMeshEventArg( CMeshEventArg const & );
	CMeshEventArg &operator =( CMeshEventArg const & );
};

/////////////////////////////////////////////////////////////////////////////////////
// Header Protection
#endif // _MESHMANAGER_H_
